<?php
declare(strict_types=1);

namespace app\common\exception;


use app\common\traits\ResponseJson;
use think\db\exception\PDOException;
use think\exception\Handle;
use think\Request;
use think\Response;
use think\facade\Log;
use think\facade\Db;
use Throwable;

class ExceptionHandle extends Handle
{
    use ResponseJson;

    /**
     * Render an exception into an HTTP response.
     * @access public
     *
     * @param Request $request
     * @param Throwable $e
     *
     * @return Response
     */
    public function render($request, Throwable $e): Response
    {
        $response = null;
        // error json返回
        if ($e instanceof FailException) {
            $response = $this->error($e->getMessage(), $e->getData(), $e->getCode(), $e->getHeader());
        }
        // success json 返回
        if ($e instanceof CorrectException) {
            $response = $this->success($e->getMessage(), $e->getData(), $e->getCode(), $e->getHeader());
        }

        if ($response === null) {
            $response = parent::render($request, $e);
        }

        if ($this->isDebugEnabled()) {
            if (method_exists($response, 'getData')) {
                $data = $response->getData();
                if (is_array($data)) {
                    $data['_debug'] = $this->getDebugInfo($e);
                    $response->data($data);
                }
            }
        }

        if (app()->isDebug()) {
            $this->errorLog($e);
        }

        return $response;
    }

    /**
     * 判断是否开启调试模式
     * @return bool
     */
    private function isDebugEnabled(): bool
    {
        return app()->isDebug();
    }

    /**
     * 获取调试信息
     * @param Throwable $e
     * @return array
     */
    private function getDebugInfo(Throwable $e): array
    {
        return [
            'name' => get_class($e),
            'file' => $e->getFile(),
            'line' => $e->getLine(),
            'code' => $e->getCode(),
            'message' => $e->getMessage(),
            'trace' => $e->getTrace(),
            'source' => $this->getSourceCode($e),
            'lastSql' => Db::getLastSql(),
            'request' => request()->pathinfo()
        ];
    }

    /**
     * 将异常写入日志
     * @param Throwable $e
     */
    private function errorLog(Throwable $e): void
    {
        $data = [
            'file' => $e->getFile(),
            'line' => $e->getLine(),
            'message' => $e->getMessage(),
            'status' => $e->getCode(),
            'lastSql' => Db::getLastSql()
        ];

        $log = $this->getVisitor();
        $log .= "\r\n" . "[ message ] [{$data['status']}] {$data['message']}";
        $log .= "\r\n" . "[ file ] {$data['file']}:{$data['line']}";
        $log .= "\r\n" . '[ header ] ' . print_r(request()->header(), true);
        $log .= '[ param ] ' . print_r(request()->param(), true);

        if ($e instanceof PDOException) {
            $log .= "[ Error SQL ] " . $e->getData()['Database Status']['Error SQL'];
            $log .= "\r\n";
        }

        $log .= "\r\n" . $e->getTraceAsString();
        $log .= "\r\n" . '--------------------------------------------------------------------------------------------';

        Log::record($log, 'error');
    }

    /**
     * 获取请求路径信息
     * @return string
     */
    private function getVisitor(): string
    {
        $data = [request()->ip(), request()->method(), request()->url(true)];
        return implode(' ', $data);
    }
}
